# vim:ts=4:sw=4:noet:
""" Project browser view (tree/sidebar) """

import wx
import os
import common
from c.compiler.spy import *
from wx.lib import dialogs
from c.ui.projectcontroller import *
from c.ui.wxw.dialogs import NewClassDialog, NewMethodDialog, RenameMethodDialog
from c.ui.wxw.ids import ids
from c.ui.wxw.events import *

class ProjectBrowser(wx.TreeCtrl):
	def __init__(self, parent, mainwin, mem):
		wx.TreeCtrl.__init__(
				self, parent, -1, 
				wx.DefaultPosition, wx.DefaultSize, 
				wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT)
		self.il = wx.ImageList(16,16)
		self.addIcons({
			"package":      "package.png",
			"category":     "bricks.png",
			"class":        "brick.png",
			"method":       "cog.png",
			"cmethod":      "cog_go.png",
			"method-error": "cog_error.png",
			"robot":        "car.png",
			"robots":       "server.png",
			})
		self.SetImageList(self.il)
		self.SetIndent(10)
		self.mw = mainwin
		if wx.Platform == "__WXMAC__":
			self.SetBackgroundColour(wx.Color(0xD1,0xD7,0xE2))

		self.root = self.AddRoot("root")
		self.SetPyData(self.root, None)
		self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
		self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)

		self.mem = mem
		self.controller = ProjectController(self, mainwin, mem)

		self.lastSelected = None
		self.libEditable = self.mw.controller.isLibraryEditable()

	def __checkLastSelected(self):
		if not self.lastSelected:
			self.lastSelected = self.GetSelection()
		return self.GetPyData(self.lastSelected)

	def setLibraryEditable(self, v):
		self.libEditable = v
		item = self.GetSelection()
		if item:
			self.controller.selectItem(item)

	def addIcons(self, ih):
		self.icons = {}
		for name in ih:
			self.icons[name] = self.il.Add(common.loadWxImage(ih[name]))

	def setItemIcon(self, item, icon):
		self.SetItemImage(item, self.icons[icon], which = wx.TreeItemIcon_Normal)

	def addItem(self, itemName, parent=None, icontype="package", pdata=None):
		if not parent:
			parent = self.root
		child = self.AppendItem(parent, itemName)
		self.SetPyData(child, pdata)
		if wx.Platform == "__WXMAC__":
			self.SetItemFont(child, wx.Font(
				11,
				wx.FONTFAMILY_DEFAULT,
				wx.FONTSTYLE_NORMAL,
				wx.FONTWEIGHT_NORMAL))
		self.SetItemImage(child, self.icons[icontype], which = wx.TreeItemIcon_Normal)
		return child
	
	def OnCompareItems(self, item1, item2):
		it1 = self.GetPyData(item1).type
		it2 = self.GetPyData(item2).type
		if it1 < it2: return -1
		if it1 > it2: return 1
		t1 = self.GetItemText(item1)
		t2 = self.GetItemText(item2)
		if t1 < t2: return -1
		if t1 > t2: return 1
		return 0

	def OnRightClick(self, event):
		itemId = self.HitTest(event.GetPoint())[0]
		self.lastSelected = itemId
		# only do this part the first time so the events are only bound once
		if not hasattr(self, "getInfoId"):
			self.getInfoId = ids.VIEW_GET_INFO
			self.renameId = wx.NewId()
			self.deleteId = ids.FILE_DELETE
			self.newClassId = ids.PROJECT_NEW_CLASS
			self.newMethodId = ids.PROJECT_NEW_METHOD
			self.subclassId = wx.NewId()
			self.disconnectId = wx.NewId()
			self.exportClassId = ids.PROJECT_EXPORT_CLASS
			self.exportPackageId = ids.PROJECT_EXPORT_PACKAGE

			self.Bind(wx.EVT_MENU, self.OnGetInfo, id=self.getInfoId)
			self.Bind(wx.EVT_MENU, self.OnRename, id=self.renameId)
			self.Bind(wx.EVT_MENU, self.OnDelete, id=self.deleteId)
			self.Bind(wx.EVT_MENU, self.OnNewMethod, id=self.newMethodId)
			self.Bind(wx.EVT_MENU, self.OnSubclass, id=self.subclassId)
			self.Bind(wx.EVT_MENU, self.OnExportClass, id=self.exportClassId)
			self.Bind(wx.EVT_MENU, self.OnExportPackage, id=self.exportPackageId)

		itype = self.GetItemPyData(itemId).type
		iname = self.GetItemPyData(itemId).name

		obj = self.GetItemPyData(itemId).obj
		menu = wx.Menu()
		show = False

		if itype == "package" and iname == "Project":
			menu.Append(self.newClassId, "New Class...")
			menu.Append(self.exportPackageId, "Export ...")
			show = True
		elif itype == "class":
			if self.libEditable or (not obj.inLibrary()):
				menu.Append(self.newMethodId, "New Method...")
				menu.AppendSeparator()
				menu.Append(self.subclassId, "Subclass...")
				menu.Append(self.deleteId, "Delete")
				menu.Append(self.exportClassId, "Export")
			else:
				menu.Append(self.subclassId, "Subclass...")
				menu.Append(self.exportClassId, "Export")
			show = True
		elif itype == "cmethod":
			if self.libEditable or (not obj.incls.inLibrary()):
				menu.Append(self.deleteId, "Delete")
				menu.Append(self.renameId, "Rename...")
				show = True
		elif itype == "method":
			if self.libEditable or (not obj.incls.inLibrary()):
				menu.Append(self.deleteId, "Delete")
				menu.Append(self.renameId, "Rename...")
				show = True
		elif itype == "robot":
			menu.Append(self.getInfoId, "Get Info")
			show = True

		if show:
			self.PopupMenu(menu)
			menu.Destroy()

	def OnSelChanged(self, event):
		self.controller.selectItem(event.GetItem())
	
	def OnRename(self, event):
		pitem = self.__checkLastSelected()
		if pitem:
			clsitem = pitem.clsitem
		oldsig = pitem.obj.sig
		oldname = pitem.obj.name
		isclsmeth = pitem.obj.incls.name[:4]=="Meta" 
		while 1:
			dialog = RenameMethodDialog(self.mw, clsitem.obj, oldsig, isclsmeth)
			res = dialog.ShowModal()
			if res == wx.ID_OK:
				x, methsig = dialog.GetValue()  # ignore isclsmeth for now
				cls = clsitem.obj
				methtype = "method"
				if isclsmeth:
					cls = cls.cls
					methtype = "cmethod"
				methcount = len(cls.methods)
				if cls.hasMethod(methsig):
					dialogs.alertDialog(dialog,
							"Class '%s' already has a method '%s'" % (cls.name, methsig),
							"Method Exists")
				else:
					self.mw.controller.compiler.deleteCachedIRMethod(cls.name, oldname)
					meth = self.controller.newMethod(cls, methsig)
					meth.source = pitem.obj.source
					meth.docs = pitem.obj.docs
					self.mw.onProjectSelectItem(meth)
					cls.removeMethod(pitem.name)
					self.controller.removeMethodItem(pitem)
					self.controller.addMethodItem(clsitem, meth, methtype)
					self.mem.removeMessage(oldname)
					self.mem.addMessage(meth.name)
					cls.addMethod(meth.name, meth)
					pitem = self.GetPyData(self.GetSelection())
					pitem.deselect(forceCompile=True)
					break
				dialog.Destroy()
			else:
				dialog.Destroy()
				break
		self.lastSelected = None
		self.controller.changed = True

	def OnSubclass(self, event):
		pitem = self.__checkLastSelected()
		self.OnNewClass(None, pitem.name)
		self.lastSelected = None

	def OnDelete(self, event):
		pitem = self.__checkLastSelected()
		pitem.deleted = True
		if pitem:
			if pitem.type=="method":
				self.controller.selectItem(pitem.clsitem.ui)
				cls = pitem.clsitem.obj
				cls.removeMethod(pitem.name)
				self.controller.removeMethodItem(pitem)
				self.mw.controller.deleteMethod(pitem.obj)
			elif pitem.type=="cmethod":
				self.controller.selectItem(pitem.clsitem.ui)
				cls = pitem.clsitem.obj.cls
				cls.removeMethod(pitem.name)
				self.controller.removeMethodItem(pitem)
				self.mw.controller.deleteMethod(pitem.obj)
			elif pitem.type=="class":
				self.controller.selectItem(pitem.pkgitem.ui)
				self.mem.removeGlobal(pitem.name)
				self.mem.removeGlobal("Meta"+pitem.name)
				self.controller.removeClassItem(pitem)
		self.lastSelected = None
	
	def OnGetInfo(self, event):
		pitem = self.__checkLastSelected()
		self.controller.getInfo(pitem)
		self.lastSelected = None

	def OnExportClass(self, event):
		pitem = self.__checkLastSelected()
		if pitem and pitem.type=="class":
			pitem.obj.fileOut(os.getcwd())
	
	def OnExportPackage(self, event):
		pitem = self.__checkLastSelected()
		if pitem and pitem.type=="package":
			dial = wx.DirDialog(self.mw, "Select Export Directory", os.getcwd())
			res = dial.ShowModal()
			if res == wx.ID_OK:
				self.controller.exportPackage(self.lastSelected, dial.GetPath())

	def OnNewMethod(self, event):
		pitem = self.__checkLastSelected()
		defclass = None
		if pitem:
			if pitem.type=="class":
				defclass = pitem.name
			elif "method" in pitem.type:
				defclass = pitem.clsitem.name
		classes = self.controller.getEditableClasses()
		if defclass not in classes:
			defclass = None
		methsig = ""
		isclsmeth = False
		while 1:
			dialog = NewMethodDialog(self.mw, classes, defclass, methsig, isclsmeth)
			res = dialog.ShowModal()
			if res == wx.ID_OK:
				clsname, isclsmeth, methsig = dialog.GetValue()
				defclass = clsname
				clsitem = self.controller.getProjectItem(clsname)
				createOk, meth, methtype = self.controller.createNewMethod(
																										methsig, clsname, isclsmeth)
				if not createOk:
					if meth == "toomany":
						dialogs.alertDialog(dialog,
								"Class '%s' has too many methods" % (clsitem.obj.name),
								"Too Many Methods")
						dialog.Destroy()
						break
					elif meth == "exists":
						dialogs.alertDialog(dialog,
								"Class '%s' already has a method '%s'" % (clsitem.obj.name, methsig),
								"Method Exists")
				else:
					self.controller.addMethodItem(clsitem, meth, methtype)
					break
				dialog.Destroy()
			else:
				dialog.Destroy()
				break
		self.lastSelected = None
		self.controller.changed = True

	def OnNewClass(self, event, supername="Object"):
		clsname = ""
		classes = self.controller.getAllClasses()
		while 1:
			dialog = NewClassDialog(self.mw, classes, supername, clsname)
			res = dialog.ShowModal()
			if res == wx.ID_OK:
				supername, clsname = dialog.GetValue()
				if clsname in classes:
					dialogs.alertDialog(dialog,
							"Class '%s' already exists" % (clsname),
							"Class Exists")
				else:
					cls = self.controller.createNewClass(clsname, supername)
					self.mem.addGlobal("Meta"+clsname, cls.cls)
					pkg = self.controller.projectPkg
					self.controller.addClassItem(pkg, cls)
					break
				dialog.Destroy()
			else:
				dialog.Destroy()
				break
